﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UtilsLib.UtilsLog;
using UtilsLib.UtilsType;

namespace UtilsLib.UtilsString
{
    public class StringParser
    {
        /// <summary>
        /// The Text to Parse
        /// </summary>
        public string OriginalText { get; set; }
        /// <summary>
        /// The text prepared for the parser
        /// </summary>
        public string Text { get; set; }
        /// <summary>
        /// The Text put to the parser's format.
        /// </summary>
        public string ParseText { get; set; }
        /// <summary>
        /// The max Length of a word.
        /// </summary>
        public int MaxWordLength { get; set; }
        /// <summary>
        /// The min length of a word.
        /// </summary>
        public int MinWordLength { get; set; }
        /// <summary>
        /// The current index of the parser in the Text.
        /// </summary>
        public int CurrentIndex { get; set; }
        /// <summary>
        /// The Text length.
        /// </summary>
        public int TextLength { get; set; }
        /// <summary>
        /// The words you want to replace in the original text.
        /// </summary>
        public Dictionary<string, string> ReplaceWords { get; set; }
        /// <summary>
        /// The words you want to identify in the original Text. Those word must be in key format (no space or funky char).
        /// </summary>
        public Dictionary<string, int> DicoResearch { get; set; }
        /// <summary>
        /// The parsing type you wish to perform.
        /// </summary>
        public ParseType TypeParsing { get; set; }
        /// <summary>
        /// The langage of the text.
        /// </summary>
        public Language Lang { get; set; }
        /// <summary>
        /// Set this to true if you wish to not take into account the stop words. (de, d'un, des, ... )
        /// </summary>
        public bool RemoveStopWords { get; set; }
        /// <summary>
        /// Set this to true if your Original Text has HTML encoded char.
        /// </summary>
        public bool ConvertFromHTMLString { get; set; }
        /// <summary>
        /// Indicate if the parser has finished the whole Text.
        /// </summary>
        public bool Ended { get; set; }
        /// <summary>
        /// Set the delimiter you wish to replace the "blank space" char.
        /// </summary>
        public string Delimiter { get; set; }


        public bool HasError { get; set; }
        public string ErrorMsg { get; set; }
        public bool Init { get; set; }


        public StringParser()
        {
            HasError = false;
            ErrorMsg = "";
            Lang = Language.French;
            RemoveStopWords = false;
            ConvertFromHTMLString = false;
            CurrentIndex = 0;
            ReplaceWords = new Dictionary<string, string>();
            DicoResearch = new Dictionary<string, int>();
            TypeParsing = ParseType.Word;
            MaxWordLength = 0;
            MinWordLength = 0;
            Init = false;
            Ended = false;

        }

        /// <summary>
        /// Reset the parser to it's original values.
        /// </summary>
        public void ResetParser()
        {
            this.InitParser();
        }

        /// <summary>
        /// Init the values of the parser.
        /// </summary>
        private void InitParser()
        {
            try
            {
                this.Text = (this.OriginalText == null ? "" : this.OriginalText);

                if (this.ConvertFromHTMLString)
                    this.Text = StringHelper.ConvertHTMLStringToString(this.Text);

                
                foreach (string key in ReplaceWords.Keys)
                    this.Text = this.Text.Replace(key, ReplaceWords[key]);

                this.ParseText = StringHelper.RemoveDiacritics(this.Text.ToLower());

                if (this.RemoveStopWords)
                    this.ParseText = StopWord.RemoveStopWords(this.ParseText, this.Delimiter, this.Lang, false);
                else
                    this.ParseText = StopWord.RemovePunctuation(this.ParseText, this.Delimiter, false);

                this.CurrentIndex = 0;
                this.TextLength = Text.Length;
                this.MaxWordLength = (MaxWordLength == 0 ? TextLength : MaxWordLength);
                this.Init = true;
                this.Ended = false;

            }
            catch (Exception ex)
            {
                LogToFile.WriteException(ex);
                this.HasError = true;
                this.ErrorMsg = ex.Message;
            }

        }

        /// <summary>
        /// Get the Next word of the text.
        /// </summary>
        /// <returns>The next word.</returns>
        public ParseResult GetNextWord()
        {
            if (!Init)
                InitParser();

            ParseResult result;
            switch (TypeParsing)
            {
                case ParseType.LongestWord:
                    result = LongestNextWord();
                    break;
                default:
                    result = NextWord();
                    break;
            }

            if (this.CurrentIndex >= this.TextLength)
                Ended = true;

            return result;

        }

        /// <summary>
        /// Get the next longest word in the text. This Word is identified with the research Dictionary.
        /// </summary>
        /// <returns>the next longest word</returns>
        private ParseResult LongestNextWord()
        {
            ParseResult result = new ParseResult();

            int trimRight = 0;
            int trimLeft = 0;
            int totalTrimLeft = 0;

            string WordKey = "";

            
            string ParseWord = StringHelper.GetWordsBetweenPositions(this.ParseText, this.CurrentIndex, this.CurrentIndex + this.MaxWordLength, this.Delimiter);
            string Word = this.Text.Substring(this.CurrentIndex, ParseWord.Length);

            while (result.IdMatch == 0)
            {
                trimRight = 0;
                trimLeft = 0;
                result.nbBoucles += 1;
                if (StringHelper.TrimString(ParseWord, this.Delimiter).Length >= this.MinWordLength)
                {
                    WordKey = ParseWord;
                    trimLeft = StringHelper.GetNbToTrimStringLeft(WordKey, this.Delimiter);
                    trimRight = StringHelper.GetNbToTrimStringRight(WordKey, this.Delimiter);
                    WordKey = StringHelper.TrimString(WordKey, this.Delimiter);

                    if (this.DicoResearch.ContainsKey(WordKey))
                    {
                        result.IdMatch = this.DicoResearch[WordKey];
                        result.Word = Word;
                        result.WordParsed = ParseWord;
                        result.WordKey = WordKey;
                        result.IndexStart = this.CurrentIndex;
                        result.IndexEnd = this.CurrentIndex + ParseWord.Length;
                        result.Found = true;
                    }
                }

                if (result.IdMatch == 0)
                {
                    if (ParseWord.IndexOf(this.Delimiter) == -1)
                    {
                        result.Word = (Word == "" ? this.Text.Substring(this.CurrentIndex) : Word);
                        result.WordParsed = ParseWord;
                        result.WordKey = WordKey;
                        result.IndexStart = this.CurrentIndex;
                        result.IndexEnd = this.CurrentIndex + result.Word.Length;


                        break;
                    }
                    ParseWord = StringHelper.TrimString(ParseWord.Substring(0, ParseWord.LastIndexOf(this.Delimiter)), this.Delimiter);
                    Word = this.Text.Substring(this.ParseText.IndexOf(ParseWord), ParseWord.Length);

                    totalTrimLeft += trimLeft;
                }
            }
            result.TrimLeft = totalTrimLeft;
            result.IndexTrimLeftStart = this.CurrentIndex;
            this.CurrentIndex = result.IndexEnd + totalTrimLeft;
            return result;
        }

        private ParseResult NextWord()
        {
            ParseResult result = new ParseResult();

            return result;
        }

    }

    /// <summary>
    /// Result object where you can find all the informations about the next word.
    /// </summary>
    public class ParseResult
    {
        public string Word { get; set; }
        public string WordParsed { get; set; }
        public string WordKey { get; set; }
        public string TrimedLeft { get; set; }
        public int IdMatch { get; set; }
        public int IndexStart { get; set; }
        public int IndexEnd { get; set; }
        public int IndexTrimLeftStart { get; set; }
        public int IndexTrimLeftEnd { get; set; }
        public int TrimLeft { get; set; }
        public int nbBoucles { get; set; }
        public bool Found { get; set; }

        public ParseResult()
        {
            IdMatch = 0;
            Found = false;
            nbBoucles = 0;
        }
    }

}
